namespace ScrumSprintMonitor.ServerAdapter.TFS.TemplateAdaptors
{
    #region #using Directives

    using System;
    using System.ComponentModel;
    using System.Diagnostics;

    using Infrastructure;

    using Isolation.TeamFoundationServer;

    #endregion

    [DebuggerDisplay("#{Id}, Type: {Type}, State: {State}, ChangedBy: {ChangedBy}", Name = "IWorkItemAdapter", Target = typeof(IWorkItemAdapter))]
    public abstract class CoreWorkItemAdapter : IWorkItemAdapter
    {
        private string assignedTo;
        private string changedBy;
        private DateTime changedDate;
        private double doneWork;
        private int id;
        private double remainingWork;
        private WorkItemState state;
        private string team;
        private string title;
        private WorkItemType type;
        private IWorkItem workItem;

        protected CoreWorkItemAdapter(IWorkItem workItem)
        {
            this.workItem = workItem;
        }

        protected IWorkItem WorkItem
        {
            [DebuggerStepThrough]
            get { return this.workItem; }
        }

        protected virtual void RetrieveOwner()
        {
            AssignedTo = this.workItem.AssignedTo;
        }

        protected virtual void RetrieveTeam()
        {
        }

        protected abstract void RetrieveType();
        protected abstract void RetrieveState();
        protected abstract void RetrieveDoneWork();
        protected abstract void RetrieveRemainingWork();

        public override bool Equals(object obj)
        {
            var other = obj as IWorkItemAdapter;

            if (other == null)
            {
                return false;
            }

            if (other.ChangedDate != ChangedDate)
            {
                return false;
            }
            if (other.ChangedBy != ChangedBy)
            {
                return false;
            }
            if (other.Team != Team)
            {
                return false;
            }
            if (other.AssignedTo != AssignedTo)
            {
                return false;
            }
            if (other.Id != Id)
            {
                return false;
            }
            if (other.State != State)
            {
                return false;
            }
            if (other.Type != Type)
            {
                return false;
            }
            if (!Equals(other.DoneWork, DoneWork))
            {
                return false;
            }
            if (!Equals(other.RemainingWork, RemainingWork))
            {
                return false;
            }
            if (other.Title != Title)
            {
                return false;
            }

            return true;
        }

        public override int GetHashCode()
        {
            return (this.workItem != null ? this.workItem.GetHashCode() : 0);
        }

        #region Implementation of IWorkItemAdapter

        public int Id
        {
            [DebuggerStepThrough]
            get { return this.id; }
        }

        public WorkItemType Type
        {
            [DebuggerStepThrough]
            get { return this.type; }
            protected set
            {
                if (value != Type)
                {
                    this.type = value;

                    OnPropertyChanged("Type");
                }
            }
        }

        public WorkItemState State
        {
            [DebuggerStepThrough]
            get { return this.state; }
            protected set
            {
                if (value != this.state)
                {
                    this.state = value;

                    OnPropertyChanged("State");
                }
            }
        }

        public double RemainingWork
        {
            [DebuggerStepThrough]
            get { return this.remainingWork; }
            protected set
            {
                if (value != this.remainingWork)
                {
                    this.remainingWork = value;

                    OnPropertyChanged("RemainingWork");
                }
            }
        }

        public double DoneWork
        {
            [DebuggerStepThrough]
            get { return this.doneWork; }
            protected set
            {
                if (value != this.doneWork)
                {
                    this.doneWork = value;

                    OnPropertyChanged("DoneWork");
                }
            }
        }

        public string Title
        {
            [DebuggerStepThrough]
            get { return this.title; }
            private set
            {
                if (value != this.title)
                {
                    this.title = value;

                    OnPropertyChanged("Title");
                }
            }
        }

        public string Team
        {
            [DebuggerStepThrough]
            get { return this.team; }
            protected set
            {
                if (value != this.team)
                {
                    this.team = value;

                    OnPropertyChanged("Team");
                }
            }
        }

        public string AssignedTo
        {
            [DebuggerStepThrough]
            get { return this.assignedTo; }
            protected set
            {
                if (value != this.assignedTo)
                {
                    this.assignedTo = value;

                    OnPropertyChanged("AssignedTo");
                }
            }
        }

        public string ChangedBy
        {
            [DebuggerStepThrough]
            get { return this.changedBy; }
            private set
            {
                if (value != this.changedBy)
                {
                    this.changedBy = value;

                    OnPropertyChanged("ChangedBy");
                }
            }
        }

        public DateTime ChangedDate
        {
            [DebuggerStepThrough]
            get { return this.changedDate; }
            private set
            {
                if (value != this.changedDate)
                {
                    this.changedDate = value;

                    OnPropertyChanged("ChangedDate");
                }
            }
        }

        public void Initialize()
        {
            try
            {
                this.id = this.workItem.Id;

                Title = this.workItem.Title;
                ChangedBy = this.workItem.ChangedBy;
                ChangedDate = this.workItem.ChangedDate;

                RetrieveType();
                RetrieveOwner();
                RetrieveTeam();
                RetrieveState();

                RetrieveRemainingWork();
                RetrieveDoneWork();
            }
            catch (TfsConnectionException e)
            {
                throw new ConnectionException(e);
            }
        }

        public void UpdateFrom(IWorkItemAdapter sourceAdapter)
        {
            if (sourceAdapter.Id != Id)
            {
                throw new ArgumentException("The sourceAdapter does not refer to the same work item!", "sourceAdapter");
            }

            var sourceCoreWorkItemAdapter = (CoreWorkItemAdapter) sourceAdapter;

            this.workItem = sourceCoreWorkItemAdapter.WorkItem;

            Title = sourceCoreWorkItemAdapter.Title;
            Team = sourceCoreWorkItemAdapter.Team;
            AssignedTo = sourceCoreWorkItemAdapter.AssignedTo;
            ChangedDate = sourceCoreWorkItemAdapter.ChangedDate;
            ChangedBy = sourceCoreWorkItemAdapter.ChangedBy;
            DoneWork = sourceAdapter.DoneWork;
            RemainingWork = sourceAdapter.RemainingWork;
            State = sourceAdapter.State;
            Type = sourceAdapter.Type;
        }

        #endregion

        #region INotifyPropertyChanged members

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}